// S-Expression data structure and simple parser
// Copyright 2004 (C) Ralph Thomas

#ifndef SEXPRESSION_H
#define SEXPRESSION_H

#include <util/string.h>

namespace util {
	//
	/// The sexpression class represents an item in a LISP S-Expression
	/// and has a static constructor which is able to create an 
	/// sexpression (and it's children) from a string. The class also
	/// provides basic typing facilities (string, int, nil, parent).
	///
	/// An sexpression object either has a value (string, int or null) or
	/// it has child sexpression objects. An sexpression object can't have
	/// both a value and children.
	//
	class sexpression {
	  public:
		//
		/// Error codes from parsing. Often an IMAP server will give
		/// out totally invalid output, or might put output which has
		/// newlines or invalid characters in it. The client really
		/// needs to know what happened, in case it can be easily
		/// fixed by (say) reading in the next line.
		//
		enum errorCodes {
			kOk,			///< Everything is ok
			kUnexpectedQuotes,	///< Unexpected double quotes
			kInvalidChar,		///< Invalid character found
			kInvalidToken,		///< Junk token found
			kUnbalancedParens	///< Unbalanced parentheses
		};
		//
		// sexpression* fromString( util::string expr, unsigned int& error )
		//
		/// Return a new sexpression object which represents the
		/// sexpression in the given string. The string must contain
		/// parentheses at the beginning and at the end, if the string
		/// has no parentheses, or has parentheses which don't match
		/// then NULL is returned.
		///
		/// \param	expr	the s-expression in string form
		/// \param	error	the error code, if NULL was returned
		///
		/// \return	a new sexpression object, or NULL.
		/// \sa		errorCodes
		//
		static sexpression* fromString( util::string expr, unsigned int& error );

		//
		// sexpression* fromStringModify( util::string& expr, unsigned int& error )
		//
		/// Return a new sexpression object which represents the
		/// sexpression in the given string. The string is modified
		/// and the S-Expression is removed from it (so when it is
		/// returned it will have the returned S-Expression removed).
		/// In the case of error, the string will not be modified but
		/// NULL will be returned.
		///
		/// \param	expr	the S-Expression in string form
		/// \param	error	the error code, if something went wrong.
		///
		/// \return	a new sexpression object or NULL.
		/// \sa		errorCodes
		//
		static sexpression* fromStringModify( util::string& expr, unsigned int& error );

		//
		// ~sexpression()
		//
		/// Virtual destructor.
		//
		virtual ~sexpression();

		//
		/// The childIterator is the only mechanism to access the
		/// children of an sexpression object. Only sexpression objects
		/// who have type "kTypeParent" may have children.
		//
		class childIterator {
		  public:
			//
			// ~childIterator()
			//
			/// Virtual destructor.
			//
			virtual ~childIterator();

			//
			// const sexpression* borrowNextChild()
			//
			/// Return a const pointer to the next child. When all
			/// of the children have been iterated through, NULL is
			/// returned.
			///
			/// \return	the next child, or NULL if there are no
			///		children left.
			//
			virtual const sexpression* borrowNextChild() = 0;
		};

		//
		// childIterator* getChildIterator() const
		//
		/// Return a new child iterator if this sexpression is of type
		/// kTypeParent. If this sexpression has a value, then NULL is
		/// returned. Note that a parent type sexpression may have zero
		/// children - which means that it represents something like
		/// "()".
		///
		/// \return	a childIterator object which starts with the
		///		first child, or NULL.
		/// \sa		getType
		//
		virtual childIterator* getChildIterator() const = 0;

		enum type {
			kTypeInt,	///< Integer type
			kTypeString,	///< String type
			kTypeNil,	///< Nil type (no value)
			kTypeParent	///< Parent type (has children)
		};

		//
		// type getType() const
		//
		/// Return the type of this sexpression. It may be kTypeInt,
		/// kTypeString, kTypeNil or kTypeParent.
		///
		/// \return	the type of this sexpression
		//
		virtual type getType() const = 0;

		//
		// util::string getString() const
		//
		/// Return the string which this sexpression represents,
		/// assuming getType() returns kTypeString. If this sexpression
		/// has no string value then the empty string ("") is returned.
		///
		/// \return	the string value of this sexpression
		//
		virtual util::string getString() const = 0;

		//
		// int getInt() const
		//
		/// Return the integer value which this sexpression represents
		/// assuming getType() returns kTypeInt. If this sexpression has
		/// no integer value then zero is returned.
		///
		/// \return	the integer value of this sexpression
		//
		virtual int getInt() const = 0;
	};
};

#endif

